
package com.tools.cleanmaster.utils;

import android.text.TextUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author yuhenghui@baidu.com
 */
public class Reflections {

    private static Field findField(Class<?> cls, String name) throws NoSuchFieldException {
        if (cls == null || TextUtils.isEmpty(name)) {
            return null;
        }
        try {
            Field m = cls.getField(name);
            if (m != null) {
                return m;
            }
        } catch (Exception e) {
            // ignore this error & pass down
        }

        Class<?> clsType = cls;
        while (clsType != null) {
            try {
                Field m = clsType.getDeclaredField(name);
                m.setAccessible(true);
                return m;
            } catch (NoSuchFieldException e) {
            }
            clsType = clsType.getSuperclass();
        }
        throw new NoSuchFieldException();
    }

    public static Field findFieldNoThrow(Class<?> cls, String name) {
        Field f = null;
        try {
            f = findField(cls, name);
        } catch (NoSuchFieldException e) {
        }
        return f;
    }

    public static Object getValueNoThrow(Field field, Object target) {
        if (field == null) {
            return null;
        }

        Object result = null;
        try {
            result = field.get(target);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return result;
    }

    public static boolean setValueNoThrow(Field field, Object target, Object newValue) {
        if (field == null) {
            return false;
        }
        try {
            field.set(target, newValue);
            return true;
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return false;
    }

    public static Class<?> loadClassNoThrow(String className) {
        if (TextUtils.isEmpty(className)) {
            return null;
        }
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Method findMethodNoThrow(Class<?> clazz, String methodName,
                                           Class<?>... parameterTypes) {
        if (clazz == null || TextUtils.isEmpty(methodName)) {
            return null;
        }
        try {
            Method m = clazz.getMethod(methodName, parameterTypes);
            if (m != null) {
                return m;
            }
        } catch (NoSuchMethodException e) {
        }

        Class<?> clsType = clazz;
        while (clsType != null) {
            try {
                Method m = clsType.getDeclaredMethod(methodName, parameterTypes);
                if (m != null) {
                    m.setAccessible(true);
                    return m;
                }
            } catch (NoSuchMethodException e) {
            }
            clsType = clazz.getSuperclass();
        }
        return null;
    }

    public static Object invokeMethodNoThrow(Method method, Object target, Object... args) {
        if (method == null) {
            return null;
        }
        try {
            return method.invoke(target, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object invokeMethod(Method method, Object target, Object... args)
            throws Throwable {
        if (method == null) {
            return null;
        }
        try {
            return method.invoke(target, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            if (e.getTargetException() != null) {
                throw e.getTargetException();
            } else {
                e.printStackTrace();
            }
        } catch (Throwable e){
            // 避免遗漏的问题导致奔溃
            e.printStackTrace();
        }
        return null;
    }

    public static Class<?>[] collectInterfaces(Class<?> clazz) {
        Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
        while (clazz != null) {
            Class<?>[] subInters = clazz.getInterfaces();
            if (subInters != null && subInters.length > 0) {
                for (int i = 0; i < subInters.length; i++) {
                    interfaces.add(subInters[i]);
                }
            }
            clazz = clazz.getSuperclass();
        }
        return interfaces.toArray(new Class<?>[interfaces.size()]);
    }

    public static Constructor<?> findConstructorNoThrow(Class<?> clazz, Class<?>... parameterTypes) {
        if (clazz == null) {
            return null;
        }
        try {
            return clazz.getConstructor(parameterTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            Constructor<?> constructor = clazz.getDeclaredConstructor(parameterTypes);
            constructor.setAccessible(true);
            return constructor;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Object newInstance(Constructor<?> constructor, Object... args) {
        if (constructor == null) {
            return null;
        }
        try {
            return constructor.newInstance(args);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String toString(Object object) {
        StringBuilder builder = new StringBuilder();
        if (object == null) {
            return "object is null";
        }

        Field[] fields = object.getClass().getDeclaredFields();
        if (fields == null || fields.length == 0) {
            return object.toString();
        }

        Class<?> clazz = object.getClass();
        builder.append("Hierachy classes\n");
        while (clazz != null) {
            builder.append(clazz.getCanonicalName());
            builder.append("\n");
            clazz = clazz.getSuperclass();
        }

        for (Field field : fields) {
            field.setAccessible(true);
            Object value = getValueNoThrow(field, object);
            builder.append(field.getName());
            builder.append("=");
            builder.append(value);
            builder.append("\n");
        }

        builder.append("\n");

        return builder.toString();
    }

    public static String toString(Class<?>... classes) {
        if (classes == null || classes.length == 0) {
            return "there is no class";
        }

        StringBuilder builder = new StringBuilder();
        builder.append("\n");
        for (Class<?> clazz : classes) {
            builder.append(clazz.getCanonicalName());
            builder.append("\n");
        }
        builder.append("\n");
        return builder.toString();
    }
}
